package ws

import (
	"sync"

	logger "github.com/sirupsen/logrus"
	"github.com/tidwall/gjson"

	"publish-manage/internal/model"
	"publish-manage/pkg/database"
	"publish-manage/pkg/util"
)

var AgentManager *agentManager

type agentManager struct {
	agents            map[string]*model.Agent
	agentConnMap      map[string]*Conn // agentId - conn
	agentServerStatus map[string]*ServerInfo
	locker            *sync.Mutex
}

func init() {
	AgentManager = &agentManager{
		agents:            make(map[string]*model.Agent),
		agentConnMap:      make(map[string]*Conn),
		agentServerStatus: make(map[string]*ServerInfo),
		locker:            new(sync.Mutex),
	}
}

func (m *agentManager) AddAgent(agent *model.Agent, conn *Conn) {
	m.locker.Lock()
	defer m.locker.Unlock()

	a, hasAgent := m.agents[agent.Id]
	if !hasAgent {
		m.agents[agent.Id] = agent
		a = agent
	}

	c, hasConn := m.agentConnMap[a.Id]
	if hasConn {
		c.Close()
		m.agentConnMap[a.Id] = conn
	} else {
		m.agentConnMap[a.Id] = conn
	}
}

func (m *agentManager) UpdateServerInfo(agentId string, data gjson.Result) {
	info, ok := m.agentServerStatus[agentId]
	if !ok {
		info = new(ServerInfo)
		m.agentServerStatus[agentId] = info
	}
	info.Cpu = data.Get("cpu").Float()

	memory := info.Memory
	if memory == nil {
		memory = new(MemoryInfo)
		info.Memory = memory
	}
	memory.Total = data.Get("memory.total").Uint()
	memory.Available = data.Get("memory.available").Uint()
	memory.Used = data.Get("memory.used").Uint()
	memory.UsedPercent = data.Get("memory.usedPercent").Float()

	disk := info.Disk
	if disk == nil {
		disk = new(DiskUsage)
		info.Disk = disk
	}
	disk.Total = data.Get("disk.total").Uint()
	disk.Free = data.Get("disk.total").Uint()
	disk.Used = data.Get("disk.total").Uint()
	disk.UsedPercent = data.Get("disk.total").Float()
}

func (m *agentManager) NotifyAgents(appPackageId string, agentId ...string) {
	pack := &model.AppPackage{Id: appPackageId}
	if exist, err := database.DB.Get(pack); err != nil {
		logger.Error(err)
		return
	} else if !exist {
		logger.Warn("不存在的部署包:", appPackageId)
		return
	}

	for i := 0; i < len(agentId); i++ {
		m.notifyToAgent(pack, agentId[i])
	}
}

func (m *agentManager) notifyToAgent(appPackage *model.AppPackage, agentId string) {
	agentPackage := &model.AgentPackage{
		AgentId:   agentId,
		PackageId: appPackage.Id,
	}
	if exist, err := database.DB.Get(agentPackage); err != nil {
		logger.Error(err)
		return
	} else if !exist {
		logger.Warnf("代理%s无法部署该部署包%s，未进行关联", agentId, appPackage.Id)
		return
	}
	if agentPackage.Type == 0 {
		agentPackage.Type = appPackage.Type
	}
	if agentPackage.Path == "" {
		agentPackage.Path = appPackage.Path
	}
	if agentPackage.ZipDir == "" {
		agentPackage.ZipDir = appPackage.ZipDir
	}
	if agentPackage.Command == "" {
		agentPackage.Command = appPackage.Command
	}

	conn, ok := m.agentConnMap[agentId]
	if ok {
		if info, err := SendWebsocketInfo(conn, &SendInfo{
			Type: TypePublishRequest,
			Data: agentPackage,
		}); err != nil {
			logger.Error(err)
			if _, err = database.DB.Insert(&model.AgentNotifyError{
				Id:         util.GenerateDatabaseID(),
				AgentId:    agentId,
				NotifyInfo: string(info),
				ErrorInfo:  err.Error(),
			}); err != nil {
				logger.Error(err)
			}
		}
	} else {
		logger.Warn("代理[", agentId, "]未上线")
	}
}
